/**
 * Copyright 2019 吉鼎科技.
 *
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.GetListRequestMessage;
import cn.easyplatform.messages.vos.GetListVo;
import cn.easyplatform.messages.vos.component.ListPageVo;
import cn.easyplatform.messages.vos.datalist.ListGetListVo;
import cn.easyplatform.messages.vos.datalist.ListHeaderVo;
import cn.easyplatform.messages.vos.datalist.ListVo;
import cn.easyplatform.spi.service.ComponentService;
import cn.easyplatform.type.FieldVo;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.type.ListRowVo;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.exporter.excel.ExcelExporter;
import cn.easyplatform.web.exporter.pdf.PdfExporter;
import cn.easyplatform.web.ext.Runnable;
import cn.easyplatform.web.ext.*;
import cn.easyplatform.web.ext.zul.TreeExt;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.zkex.ListSupport;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.event.EventListenerHandler;
import cn.easyplatform.web.task.support.ExpressionEngine;
import cn.easyplatform.web.task.support.SupportFactory;
import cn.easyplatform.web.utils.ExtUtils;
import cn.easyplatform.web.utils.WebUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.util.media.AMedia;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Components;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.OpenEvent;
import org.zkoss.zk.ui.metainfo.EventHandler;
import org.zkoss.zk.ui.metainfo.EventHandlerMap;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.*;
import org.zkoss.zul.event.PagingEvent;
import org.zkoss.zul.event.ZulEvents;

import java.io.ByteArrayOutputStream;
import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class TreeBuilder implements ComponentBuilder, Widget, Exportable, Runnable, EventListener<Event> {

    private final static Logger log = LoggerFactory.getLogger(TreeBuilder.class);

    //默认分组
    private final static int DEFAULT = 0;
    //具有上下层结构
    private final static int LEVEL = 1;
    //延时加载
    private final static int GROUP = 2;
    //双查询
    private final static int DUAL = 3;

    private TreeExt tree;

    private OperableHandler main;

    private int type = DEFAULT;

    private Paging paging;

    private String eventName;

    private ListSupport support;

    private Component anchor;

    public TreeBuilder(OperableHandler mainTaskHandler, TreeExt tree) {
        this.main = mainTaskHandler;
        this.tree = tree;
    }

    public TreeBuilder(ListSupport support, TreeExt tree, Component anchor) {
        this.support = support;
        this.main = support.getMainHandler();
        this.tree = tree;
        this.anchor = anchor;
    }

    @Override
    public Component build() {
        if (tree.isImmediate())
            verify();
        tree.setAttribute("$proxy", this);
        //表示原生的，不需要设置
        if (tree.getTreecols() != null)
            tree.getTreecols().setAttribute("o", 1);
        createHeads();
        Component c = tree;
        if (tree.getPageSize() > 0) {
            if (tree.isFetchAll()) {
                tree.setMold("paging");
                paging = tree.getPagingChild();
            } else {
                Vlayout layout = new Vlayout();
                layout.setSpacing("0");
                if (!Strings.isBlank(tree.getWidth())) {
                    layout.setWidth(tree.getWidth());
                    tree.setWidth(null);
                    tree.setHflex("1");
                } else
                    layout.setHflex(tree.getHflex());
                if (!Strings.isBlank(tree.getHeight())) {
                    layout.setHeight(tree.getHeight());
                    tree.setHeight(null);
                    layout.setVflex("1");
                } else
                    layout.setVflex(tree.getVflex());
                Components.replace(tree, layout);
                layout.appendChild(tree);
                paging = new Paging();
                paging.setHflex("1");
                paging.addEventListener(ZulEvents.ON_PAGING, this);
                layout.appendChild(paging);
                c = layout;
            }
            paging.setAutohide(true);
            paging.setDetailed(tree.isPagingDetailed());
            paging.setPageSize(tree.getPageSize());
            if (!Strings.isBlank(tree.getPagingMold()))
                paging.setMold(tree.getPagingMold());
            if (!Strings.isBlank(tree.getPagingStyle()))
                paging.setStyle(tree.getPagingStyle());
        }
        if (!Strings.isBlank(tree.getEvent())) {
            String event = tree.getEvent();
            if (Events.isValid(event)) {
                char[] cs = event.toCharArray();
                int i = 0;
                boolean isSep = false;
                StringBuilder sb = new StringBuilder();
                while (true) {
                    if (cs[i] == ':') {
                        isSep = true;
                        break;
                    }
                    if (Character.isLetter(cs[i]))
                        sb.append(cs[i]);
                    else
                        break;
                    i++;
                }
                if (isSep && sb.length() > 0) {
                    eventName = sb.toString();
                    event = event.substring(i + 1).trim();
                    tree.setEvent(event);
                }
                sb = null;
            } else
                eventName = Events.ON_CLICK;
        } else {
            EventHandlerMap em = tree.getEventHandlerMap();
            if (em != null && !em.isEmpty()) {
                em.getEventNames().forEach(name -> {
                    List<EventHandler> evts = em.getAll(name);
                    evts.forEach(eh -> {
                        if (eh.isEffective(tree)) {
                            if (Events.ON_CLICK.equals(name) || Events.ON_DOUBLE_CLICK.equals(name)) {
                                eventName = name;
                                tree.setEvent(eh.getZScript().getRawContent());
                            } else {
                                tree.addEventListener(name, new EventListenerHandler(name,
                                        support == null ? main : support, eh.getZScript().getRawContent(), anchor));
                            }
                        }
                    });
                });
            }
        }
        if (tree.isImmediate())
            load();
        return c;
    }

    private void verify() {
        if (Strings.isBlank(tree.getQuery()))
            throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<tree>", "query");
        //如果type=default时表示要分组件栏位，否则表示2个分层栏位：第1个表示本层id,第2个表示上层id
        if (Strings.isBlank(tree.getGroupField()))
            throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<tree>", "groupField");
        if (!Strings.isBlank(tree.getGroupQuery()) && tree.getGroupQuery().startsWith("$")) {
            Object val = WebUtils.getFieldValue(main, tree.getGroupQuery().substring(1));
            tree.setGroupField((String) val);
        }
        if (TreeExt.GROUP.equals(tree.getType())) {
            if (Strings.isBlank(tree.getGroupQuery()))
                type = LEVEL;
            else
                type = GROUP;
        } else if (TreeExt.DUAL.equals(tree.getType()))
            type = DUAL;
        if (tree.getGroupField().startsWith("$")) {
            Object val = WebUtils.getFieldValue(main, tree.getGroupField().substring(1));
            if (val == null || val.toString().equals(""))
                throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<tree>", "groupField");
            tree.setGroupField(val.toString());
        }
    }

    /**
     * 创建树组件标题
     */
    private void createHeads() {
        if (tree.getTreecols() != null) {
            if (tree.getTreecols().getAttribute("o") == null) {
                tree.getTreecols().detach();
                createHeader();
            }
        } else {
            createHeader();
        }
    }

    /**
     * 创建表头
     */
    private void createHeader() {
        if (!Strings.isBlank(tree.getTitle())) {
            String title = tree.getTitle();
            if (title.startsWith("$")) {
                Object val = WebUtils.getFieldValue(main, title.substring(1));
                if (val == null || val.toString().equals(""))
                    return;
                title = (String) val;
            }
            Treecols head = new Treecols();
            head.setSizable(tree.isSizable());
            String[] headers = title.split(",");
            for (String h : headers) {
                Treecol header = new Treecol(h.trim());
                if (tree.isSizedByContent())
                    header.setHflex("1");
                else
                    header.setWidth(header.getLabel().length() * 15 + "px");
                head.appendChild(header);
            }
            tree.appendChild(head);
            if (tree.getFrozen() == null && tree.getFrozenColumns() > 0) {
                Frozen frozen = new Frozen();
                if (!Strings.isBlank(tree.getFrozenStyle()))
                    frozen.setStyle(tree.getFrozenStyle());
                frozen.setColumns(tree.getFrozenColumns());
                tree.appendChild(frozen);
            }
        }
        if (tree.getTreefoot() != null)
            tree.getTreefoot().detach();
    }

    /**
     * 加载数据
     */
    private void load() {
        GetListVo gv = null;
        if (anchor != null) {
            ListRowVo rowVo = WebUtils.getAnchorValue(anchor);
            Object[] data = support.isCustom() ? rowVo.getData() : rowVo
                    .getKeys();
            gv = new ListGetListVo(tree.getDbId(), tree.getQuery(), support
                    .getComponent().getId(), data);
        } else {
            gv = new GetListVo(tree.getDbId(), tree.getQuery());
        }
        gv.setFilter(tree.getFilter());
        gv.setPageSize(tree.isFetchAll() ? 0 : tree.getPageSize());
        gv.setOrderBy(tree.getOrderBy());
        gv.setGetCount(gv.getPageSize() > 0);
        GetListRequestMessage req = new GetListRequestMessage(main.getId(), gv);
        ComponentService cs = ServiceLocator.lookup(ComponentService.class);
        IResponseMessage<?> resp = cs.getList(req);
        if (!resp.isSuccess()) {
            Clients.wrongValue(tree, (String) resp.getBody());
            return;
        }
        List<FieldVo[]> data = null;
        if (gv.isGetCount()) {
            ListPageVo lpv = (ListPageVo) resp.getBody();
            data = lpv.getData();
            paging.setTotalSize(lpv.getTotalCount());
        } else
            data = (List<FieldVo[]>) resp.getBody();
        redraw(data);
    }

    /**
     * 重绘树结点
     *
     * @param data
     */
    private void redraw(List<FieldVo[]> data) {
        if (!data.isEmpty()) {
            if (Strings.isBlank(tree.getValueField()))
                tree.setValueField(data.get(0)[0].getName());
            ExpressionEngine expressionEngine = null;
            if (!Strings.isBlank(tree.getRowScript())) {
                String script = tree.getRowScript();
                if (tree.getRowScript().startsWith("$")) {
                    script = (String) WebUtils.getFieldValue(main, tree.getRowScript().substring(1));
                    tree.setRowScript(script);
                }
                if (script != null) {
                    expressionEngine = SupportFactory.getExpressionEngine(main, script);
                    expressionEngine.compile();
                }
            }
            try {
                Node root = null;
                if (type == DEFAULT)
                    root = createDefaultModel(data);
                else
                    root = createGroupModel(data);
                Treechildren tc = new Treechildren();
                for (Node node : root.getChildren()) {
                    Treeitem ti = new Treeitem();
                    if (node.getData() == null) {//type=DEFAULT
                        ti.setLabel((String) node.getId());
                    } else {
                        createRow(expressionEngine, ti, node.getData());
                        if (eventName != null && type != DUAL)
                            ti.addEventListener(eventName, this);
                    }
                    ti.setValue(node);
                    if (!node.isLeaf()) {
                        ti.appendChild(new Treechildren());
                        if (tree.isLazy()) {
                            if (tree.getDepth() > 0)
                                createNode(expressionEngine, ti, node.getChildren());
                            else {
                                ti.addEventListener(Events.ON_OPEN, this);
                                ti.setOpen(false);
                            }
                        } else {
                            createNode(expressionEngine, ti, node.getChildren());
                        }//else
                    } else if (type == GROUP || tree.getDepth() > 0) {
                        ti.appendChild(new Treechildren());
                        ti.addEventListener(Events.ON_OPEN, this);
                        ti.setOpen(false);
                    }
                    tc.appendChild(ti);
                }
                tree.appendChild(tc);
            } finally {
                if (expressionEngine != null) {
                    expressionEngine.destroy();
                    expressionEngine = null;
                }
            }
        }
    }

    /**
     * 创建子结点
     *
     * @param parent
     * @param children
     */
    private void createNode(ExpressionEngine expressionEngine, Treeitem parent, List<Node> children) {
        for (Node node : children) {
            Treeitem ti = new Treeitem();
            if (node.getData() == null) {
                ti.setLabel((String) node.getId());
            } else {
                createRow(expressionEngine, ti, node.getData());
                if (eventName != null && type != DUAL)
                    ti.addEventListener(eventName, this);
            }
            ti.setValue(node);
            if (!node.isLeaf()) {
                ti.appendChild(new Treechildren());
                if (tree.isLazy()) {
                    ti.addEventListener(Events.ON_OPEN, this);
                    ti.setOpen(false);
                } else {
                    createNode(expressionEngine, ti, node.getChildren());
                    if (tree.getDepth() > 0)
                        ti.setOpen(tree.getDepth() > node.getLevel());
                }
            } else if (type == DUAL) {
                ti.addEventListener(Events.ON_OPEN, this);
                ti.appendChild(new Treechildren());
                ti.setOpen(false);
            }
            parent.getTreechildren().appendChild(ti);
        }
        if (type == DUAL)
            loadGroup(expressionEngine, parent);
    }

    /**
     * 创建行
     *
     * @param expressionEngine
     * @param ti
     * @param fvs
     */
    private void createRow(ExpressionEngine expressionEngine, Treeitem ti, FieldVo[] fvs) {
        createRow(expressionEngine, ti, fvs, false);
    }

    /**
     * 创建行
     *
     * @param expressionEngine
     * @param ti
     * @param fvs
     */
    private void createRow(ExpressionEngine expressionEngine, Treeitem ti, FieldVo[] fvs, boolean dual) {
        int cols = 1;
        if (type != DUAL || dual) {
            cols = tree.getTreecols() == null ? 1 : tree.getTreecols().getChildren().size();
            if (fvs.length < cols)
                cols = fvs.length;
        }
        Map<String, Object> data = null;
        Map<String, Component> managedComponents = null;
        if (expressionEngine != null) {
            data = new HashMap<>();
            for (FieldVo fv : fvs)
                data.put(fv.getName(), fv.getValue());
            managedComponents = new HashMap<>();
        }
        Treerow tr = new Treerow();
        tr.setParent(ti);
        for (int i = 0; i < cols; i++) {
            Object label = fvs[i].getValue();
            Treecell cell = new Treecell(label == null ? "" : label.toString());
            cell.setParent(tr);
            if (managedComponents != null)
                managedComponents.put(fvs[i].getName(), cell);
        }
        if (expressionEngine != null) {
            expressionEngine.eval(ti, data, managedComponents);
            data = null;
            managedComponents = null;
        }
    }

    /**
     * 加载第2组数据
     *
     * @param ti
     */
    protected void loadGroup(ExpressionEngine expressionEngine, Treeitem ti) {
        Node node = ti.getValue();
        if (Strings.isBlank(tree.getGroupQuery())) {
            int pos = tree.getQuery().toLowerCase().indexOf("where");
            if (pos < 0) {
                ti.getTreechildren().detach();
                return;
            } else {
                StringBuilder sb = new StringBuilder();
                sb.append(tree.getQuery().substring(0, pos)).append(" WHERE ").append(tree.getGroupField().split(",")[1]).append("=?");
                if (!Strings.isBlank(tree.getOrderBy()))
                    sb.append(" ORDER BY ").append(tree.getOrderBy());
                tree.setGroupQuery(sb.toString());
                tree.setAttribute("gg", "");
            }
        } else if (!tree.isFetchAll() && type == DUAL) {
            int pos = tree.getQuery().toLowerCase().indexOf("where");
            if (pos > 0) {
                StringBuilder sb = new StringBuilder();
                sb.append(tree.getQuery().substring(0, pos)).append(" WHERE ").append(tree.getGroupField().split(",")[1]).append("=?");
                if (!Strings.isBlank(tree.getOrderBy()))
                    sb.append(" ORDER BY ").append(tree.getOrderBy());
                GetListVo gv = null;
                if (anchor != null) {
                    ListRowVo rowVo = WebUtils.getAnchorValue(anchor);
                    Object[] data = support.isCustom() ? rowVo.getData() : rowVo
                            .getKeys();
                    gv = new ListGetListVo(tree.getDbId(), sb.toString(), support
                            .getComponent().getId(), data);
                } else {
                    gv = new GetListVo(tree.getDbId(), sb.toString());
                }
                gv.setParameter(node.getId());
                ComponentService cs = ServiceLocator.lookup(ComponentService.class);
                IResponseMessage<?> resp = cs.getList(new GetListRequestMessage(main.getId(), gv));
                if (!resp.isSuccess())
                    MessageBox.showMessage(resp);
                else {
                    List<FieldVo[]> data = (List<FieldVo[]>) resp.getBody();
                    if (!data.isEmpty()) {
                        Integer[] gidx = new Integer[2];
                        String[] groupFields = tree.getGroupField().split(",");
                        FieldVo[] first = data.get(0);
                        for (int i = 0; i < first.length; i++) {
                            if (first[i].getName().equalsIgnoreCase(groupFields[0]))
                                gidx[0] = i;
                            if (first[i].getName().equalsIgnoreCase(groupFields[1]))
                                gidx[1] = i;
                        }
                        for (FieldVo[] fvs : data) {
                            Treeitem child = new Treeitem();
                            child.setValue(new Node(fvs[gidx[0]].getValue(), fvs[gidx[1]].getValue(), fvs));
                            createRow(expressionEngine, child, fvs);
                            child.appendChild(new Treechildren());
                            child.addEventListener(Events.ON_OPEN, this);
                            child.setOpen(false);
                            child.setSelectable(false);
                            ti.getTreechildren().appendChild(child);
                        }
                    }
                }
            }
        }
        GetListVo gv = null;
        if (anchor != null) {
            ListRowVo rowVo = WebUtils.getAnchorValue(anchor);
            Object[] data = support.isCustom() ? rowVo.getData() : rowVo
                    .getKeys();
            gv = new ListGetListVo(tree.getDbId(), tree.getGroupQuery(), support
                    .getComponent().getId(), data);
        } else {
            gv = new GetListVo(tree.getDbId(), tree.getGroupQuery());
        }
        gv.setParameter(node.getId());
        ComponentService cs = ServiceLocator.lookup(ComponentService.class);
        IResponseMessage<?> resp = cs.getList(new GetListRequestMessage(main.getId(), gv));
        if (!resp.isSuccess())
            MessageBox.showMessage(resp);
        else {
            List<FieldVo[]> data = (List<FieldVo[]>) resp.getBody();
            if (!data.isEmpty()) {
                int gidx = 0;
                if (type == GROUP) {
                    FieldVo[] first = data.get(0);
                    for (; gidx < first.length; gidx++) {
                        if (first[gidx].getName().equalsIgnoreCase(tree.getGroupField()))
                            break;
                    }
                } else if (tree.getAttribute("gg") != null) {
                    FieldVo[] first = data.get(0);
                    for (; gidx < first.length; gidx++) {
                        if (first[gidx].getName().equalsIgnoreCase(tree.getValueField()))
                            break;
                    }
                }
                Component parent = ti.getTreechildren();
                if (type == DUAL && !Strings.isBlank(tree.getDualName())) {
                    if (parent.getFirstChild() != null) {//有第一分组存在
                        Treeitem child = new Treeitem(tree.getDualName());
                        child.appendChild(new Treechildren());
                        child.setParent(parent);
                        parent = child.getTreechildren();
                    }
                }
                for (FieldVo[] fvs : data) {
                    Treeitem child = new Treeitem();
                    if (type == DUAL) {
                        createRow(expressionEngine, child, fvs, true);
                        child.setImage("~./images/weather_sun.png");
                        child.setValue(new Node(fvs));
                    } else {
                        createRow(expressionEngine, child, fvs);
                        child.setValue(new Node(fvs[gidx].getValue(), fvs));
                        child.appendChild(new Treechildren());
                        child.addEventListener(Events.ON_OPEN, this);
                        child.setOpen(false);
                    }
                    child.setParent(parent);
                    if (eventName != null)
                        child.addEventListener(eventName, this);
                }//for
            } else if (type == GROUP || tree.getAttribute("gg") != null) //if
                ti.getTreechildren().detach();
        }//else
    }

    /**
     * 根据数据生成分组模型结构
     *
     * @param data
     * @return
     */
    private Node createGroupModel(List<FieldVo[]> data) {
        if (type == GROUP) {
            Integer gidx = null;
            FieldVo[] first = data.get(0);
            for (int i = 0; i < first.length; i++) {
                if (first[i].getName().equalsIgnoreCase(tree.getGroupField())) {
                    gidx = i;
                    break;
                }
            }
            if (gidx != null) {
                Node root = new Node(data.size());
                for (FieldVo[] fvs : data)
                    root.appendChild(new Node(fvs[gidx].getValue(), fvs));
                return root;
            }
            return new Node(0);
        } else {
            Integer[] gidx = new Integer[2];
            String[] groupFields = tree.getGroupField().split(",");
            FieldVo[] first = data.get(0);
            for (int i = 0; i < first.length; i++) {
                if (first[i].getName().equalsIgnoreCase(groupFields[0]))
                    gidx[0] = i;
                if (first[i].getName().equalsIgnoreCase(groupFields[1]))
                    gidx[1] = i;
            }
            if (gidx[0] == null || gidx[1] == null)
                return new Node(0);
            Node root = new Node(1);
            if (tree.isFetchAll()) {
                Map<Object, Node> map = new LinkedHashMap<>();
                List<Node> pending = new ArrayList<>();
                for (FieldVo[] fvs : data) {
                    Node node = new Node(fvs[gidx[0]].getValue(), fvs[gidx[1]].getValue(), fvs);
                    Node parent = map.get(node.getParentId());
                    if (parent != null)
                        parent.appendChild(node);
                    else
                        pending.add(node);
                    map.put(node.getId(), node);
                }
                Iterator<Node> itr = pending.iterator();
                while (itr.hasNext()) {
                    Node node = itr.next();
                    Node parent = map.get(node.getParentId());
                    if (parent != null) {
                        parent.appendChild(node);
                        itr.remove();
                    } else
                        root.appendChild(node);
                }
            } else {
                for (FieldVo[] fvs : data)
                    root.appendChild(new Node(fvs[gidx[0]].getValue(), fvs[gidx[1]].getValue(), fvs));
            }
            return root;
        }
    }

    /**
     * 根据数据生成树模型
     *
     * @param data
     * @return
     */
    private Node createDefaultModel(List<FieldVo[]> data) {
        String[] groups = tree.getGroupField().split(";");
        Integer[][] gidx = new Integer[groups.length][];
        FieldVo[] first = data.get(0);
        for (int i = 0; i < groups.length; i++) {
            String group = groups[i];
            String[] fields = group.split(",");
            gidx[i] = new Integer[fields.length];
            for (int j = 0; j < fields.length; j++) {
                for (int k = 0; k < first.length; k++) {
                    if (fields[j].equalsIgnoreCase(first[k].getName())) {
                        gidx[i][j] = k;
                        break;
                    }
                }
            }
        }
        //检查是否完整
        for (int i = 0; i < gidx.length; i++) {
            for (int j = 0; j < gidx[i].length; j++) {
                if (gidx[i][j] == null)
                    return new Node(0);
            }
        }
        Node root = new Node(1);
        Map<Object, Node> map = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        StringBuilder tmp = new StringBuilder();
        for (FieldVo[] fvs : data) {
            for (int i = 0; i < gidx[0].length; i++)
                sb.append(fvs[gidx[0][i]].getValue());
            String parentId = sb.toString();
            Node parent = map.get(parentId);
            if (parent == null) {
                parent = new Node(parentId);
                map.put(parent.getId(), parent);
                root.appendChild(parent);
            }
            for (int i = 1; i < gidx.length; i++) {
                tmp.setLength(0);
                for (int j = 0; j < gidx[i].length; j++) {
                    sb.append(fvs[gidx[i][j]].getValue());
                    tmp.append(fvs[gidx[i][j]].getValue());
                }
                Node node = map.get(sb.toString());
                if (node == null) {
                    node = new Node(tmp.toString());
                    map.put(sb.toString(), node);
                    parent.appendChild(node);
                }
                parent = node;
            }
            parent.appendChild(new Node(fvs));
            sb.setLength(0);
        }
        return root;
    }

    /**
     * 导出数据
     *
     * @param type
     * @param exportHeaders
     */
    @Override
    public void export(String type, Object... exportHeaders) {
        if (tree.getTreecols() == null)
            return;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ListVo entity = new ListVo(tree.getId());
            List<Component> head = tree.getTreecols().getChildren();
            int size = head.size();
            List<ListHeaderVo> headers = new ArrayList<ListHeaderVo>(size);
            for (int i = 0; i < size; i++) {
                ListHeaderVo headerVo = new ListHeaderVo();
                headerVo.setName(((Treecol) head.get(i)).getLabel());
                if (exportHeaders.length == 0)
                    headerVo.setExport(true);
                else {
                    if (exportHeaders[0] instanceof Number) {
                        for (Object idx : exportHeaders) {
                            if (((Number) idx).intValue() == i) {
                                headerVo.setExport(true);
                                break;
                            }
                        }
                    } else if (exportHeaders[0] instanceof Strings) {
                        for (Object name : exportHeaders) {
                            if (name.equals(headerVo.getName())) {
                                headerVo.setExport(true);
                                break;
                            }
                        }
                    }
                }
                headers.add(headerVo);
            }
            entity.setHeaders(headers);
            if (type.equalsIgnoreCase("pdf")) {
                PdfExporter exporter = new PdfExporter();
                exporter.export(entity, tree, out);
                AMedia amedia = new AMedia(tree.getId() + ".pdf", "pdf", "application/pdf", out.toByteArray());
                org.zkoss.zkmax.zul.Filedownload.save(amedia);
            } else {
                ExcelExporter exporter = new ExcelExporter();
                exporter.export(entity, tree, out);
                AMedia amedia = new AMedia(tree.getId() + ".xlsx", "xlsx", "application/file", out.toByteArray());
                org.zkoss.zkmax.zul.Filedownload.save(amedia);
            }
        } catch (Exception ex) {
            if (log.isErrorEnabled())
                log.error("export {},{}", tree.getId(), ex);
            throw new RuntimeException(ex);
        } finally {
            IOUtils.closeQuietly(out);
        }
    }

    /**
     * 重新加载
     *
     * @param widget
     */
    @Override
    public void reload(Component widget) {
        verify();
        if (tree.getTreechildren() != null)
            tree.getTreechildren().detach();
        createHeads();
        load();
    }

    /**
     * 分页
     *
     * @param pageNo
     */
    private void paging(int pageNo) {
        GetListVo gv = null;
        if (anchor != null) {
            ListRowVo rowVo = WebUtils.getAnchorValue(anchor);
            Object[] data = support.isCustom() ? rowVo.getData() : rowVo
                    .getKeys();
            gv = new ListGetListVo(tree.getDbId(), tree.getQuery(), support
                    .getComponent().getId(), data);
        } else {
            gv = new GetListVo(tree.getDbId(), tree.getQuery());
        }
        gv.setFilter(tree.getFilter());
        gv.setPageSize(tree.getPageSize());
        gv.setOrderBy(tree.getOrderBy());
        gv.setPageNo(pageNo + 1);
        GetListRequestMessage req = new GetListRequestMessage(main.getId(), gv);
        ComponentService cs = ServiceLocator.lookup(ComponentService.class);
        IResponseMessage<?> resp = cs.getList(req);
        if (!resp.isSuccess()) {
            Clients.wrongValue(paging, (String) resp.getBody());
            return;
        }
        if (tree.getTreechildren() != null)
            tree.getTreechildren().detach();
        redraw((List<FieldVo[]>) resp.getBody());
    }

    /**
     * 事件处理
     *
     * @param event
     * @throws Exception
     */
    @Override
    public void onEvent(Event event) throws Exception {
        if (event instanceof OpenEvent) {
            OpenEvent oe = (OpenEvent) event;
            if (oe.isOpen()) {
                ExpressionEngine expressionEngine = null;
                if (!Strings.isBlank(tree.getRowScript())) {
                    expressionEngine = SupportFactory.getExpressionEngine(main, tree.getRowScript());
                    expressionEngine.compile();
                }
                try {
                    Treeitem ti = (Treeitem) oe.getTarget();
                    if (type == GROUP) {
                        loadGroup(expressionEngine, ti);
                    } else {
                        Node parent = ti.getValue();
                        if (parent.getChildren() == null)
                            loadGroup(expressionEngine, ti);
                        else
                            createNode(expressionEngine, ti, parent.getChildren());
                    }
                    ti.removeEventListener(Events.ON_OPEN, this);
                } finally {
                    if (expressionEngine != null) {
                        expressionEngine.destroy();
                        expressionEngine = null;
                    }
                }
            }
        } else if (event instanceof PagingEvent) {
            PagingEvent pe = (PagingEvent) event;
            paging(pe.getActivePage());
        } else if (event.getName().equals(eventName)) {
            EventListenerHandler elh = new EventListenerHandler(eventName, main, tree.getEvent(), anchor);
            elh.onEvent(new Event(eventName, tree));
        }
    }

    @Override
    public void go(Object[] args) {
        ExtUtils.go(main, tree, args);
    }
}
